<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>立方体绘制</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      canvas {
        margin: 50px auto 0;
        display: block;
        background: yellow;
      }
    </style>
    <script src="test.js"></script>
  </head>
  <body>
    <canvas id="canvas" width="400" height="400"></canvas>
    <script>
      // 获取
      const canvas = document.getElementById('canvas')
      const gl = canvas.getContext('webgl')
      // 定义片源着色器和顶点着色器
      const vsSource = `
        attribute vec4 apos;
        attribute vec4 acolor;
        varying vec4 vcolor;
        uniform mat4 mx;
        uniform mat4 my;

        void main() {
          gl_Position = mx * my * apos;
          vcolor = acolor;
        }
      `
      const fsSource = `
        precision mediump float;
        varying vec4 vcolor;

        void main() {
          gl_FragColor = vcolor;
        }
      `
      //   初始化webgl
      const program = initShader(gl, vsSource, fsSource)
      gl.useProgram(program)
      // 创建缓存区
      function initBuffer(gl, program) {
        const aposLocation = gl.getAttribLocation(program, 'apos')
        const acolorLocation = gl.getAttribLocation(program, 'acolor')

        const verticesColors = new Float32Array([
          -0.5, -0.5, 0.5, 1, 0, 0, 1, 0.5, -0.5, 0.5, 1, 0, 0, 1, 0.5, 0.5, 0.5, 1, 0, 0, 1, -0.5, 0.5, 0.5, 1, 0, 0, 1,

          -0.5, 0.5, 0.5, 0, 1, 0, 1, -0.5, 0.5, -0.5, 0, 1, 0, 1, -0.5, -0.5, -0.5, 0, 1, 0, 1, -0.5, -0.5, 0.5, 0, 1, 0, 1,

          0.5, 0.5, 0.5, 0, 0, 1, 1, 0.5, -0.5, 0.5, 0, 0, 1, 1, 0.5, -0.5, -0.5, 0, 0, 1, 1, 0.5, 0.5, -0.5, 0, 0, 1, 1,

          0.5, 0.5, -0.5, 1, 0, 1, 1, 0.5, -0.5, -0.5, 1, 0, 1, 1, -0.5, -0.5, -0.5, 1, 0, 1, 1, -0.5, 0.5, -0.5, 1, 0, 1, 1,

          -0.5, 0.5, 0.5, 1, 1, 0, 1, 0.5, 0.5, 0.5, 1, 1, 0, 1, 0.5, 0.5, -0.5, 1, 1, 0, 1, -0.5, 0.5, -0.5, 1, 1, 0, 1,

          -0.5, -0.5, 0.5, 0, 1, 1, 1, -0.5, -0.5, -0.5, 0, 1, 1, 1, 0.5, -0.5, -0.5, 0, 1, 1, 1, 0.5, -0.5, 0.5, 0, 1, 1, 1
        ])

        const indices = new Uint16Array([
          0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23
        ])

        const vertexColorBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW)

        const indexBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

        const FSIZE = verticesColors.BYTES_PER_ELEMENT
        // 启用顶点属性并指向顶点缓冲区中的数据
        gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, FSIZE * 7, 0)
        gl.enableVertexAttribArray(aposLocation)

        gl.vertexAttribPointer(acolorLocation, 4, gl.FLOAT, false, FSIZE * 7, FSIZE * 3)
        gl.enableVertexAttribArray(acolorLocation)

        render(gl, program, indices.length)
      }

      let angle = 0
      // 实现x和y轴的旋转
      function render(gl, program, count = 36) {
        // 1.清屏操作
        gl.clearColor(0, 0, 0, 1)
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
        // 2.启用深度测试0
        gl.enable(gl.DEPTH_TEST)
        //3.计算旋转角度：计算旋转角度的余弦和正弦值来实现旋转动画
        angle += 1
        const rad = (angle * Math.PI) / 180
        const cos = Math.cos(rad)
        const sin = Math.sin(rad)
        // 4.绕 X 轴的旋转矩阵
        const mx = gl.getUniformLocation(program, 'mx')
        const mxArr = new Float32Array([1, 0, 0, 0, 0, cos, -sin, 0, 0, sin, cos, 0, 0, 0, 0, 1])
        gl.uniformMatrix4fv(mx, false, mxArr)
        // 5.绕 Y 轴的旋转矩阵
        const my = gl.getUniformLocation(program, 'my')
        const myArr = new Float32Array([cos, 0, sin, 0, 0, 1, 0, 0, -sin, 0, cos, 0, 0, 0, 0, 1])
        gl.uniformMatrix4fv(my, false, myArr)

        gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, 0)
        requestAnimationFrame(() => render(gl, program, count))
      }

      initBuffer(gl, program)
    </script>
  </body>
</html>
